// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Declaration of a Windows event trace provider class, to allow using
// Windows Event Tracing for logging transport and control.
#ifndef BASE_WIN_EVENT_TRACE_PROVIDER_H_
#define BASE_WIN_EVENT_TRACE_PROVIDER_H_

#include <windows.h>
#include <wmistr.h>
#include <evntrace.h>
#include <stddef.h>
#include <stdint.h>
#include <limits>

#include "base/base_export.h"
#include "base/macros.h"

#undef max
#undef min

#if !defined(WIN32)

typedef struct  _TRACE_GUID_REGISTRATION {
    LPCGUID Guid;            // Guid of data block being registered or updated.
    HANDLE RegHandle;        // Guid Registration Handle is returned.
} TRACE_GUID_REGISTRATION, * PTRACE_GUID_REGISTRATION;

typedef ULONG64 TRACEHANDLE, *PTRACEHANDLE;

typedef struct _EVENT_TRACE_HEADER {        // overlays WNODE_HEADER
    USHORT          Size;                   // Size of entire record
    union {
        USHORT      FieldTypeFlags;         // Indicates valid fields
        struct {
            UCHAR   HeaderType;             // Header type - internal use only
            UCHAR   MarkerFlags;            // Marker - internal use only
        } DUMMYSTRUCTNAME;
    } DUMMYUNIONNAME;
    union {
        ULONG       Version;
        struct {
            UCHAR   Type;                   // event type
            UCHAR   Level;                  // trace instrumentation level
            USHORT  Version;                // version of trace record
        } Class;
    } DUMMYUNIONNAME2;
    ULONG           ThreadId;               // Thread Id
    ULONG           ProcessId;              // Process Id
    LARGE_INTEGER   TimeStamp;              // time when event happens
    union {
        GUID        Guid;                   // Guid that identifies event
        ULONGLONG   GuidPtr;                // use with WNODE_FLAG_USE_GUID_PTR
    } DUMMYUNIONNAME3;
    union {
        struct {
            ULONG   KernelTime;             // Kernel Mode CPU ticks
            ULONG   UserTime;               // User mode CPU ticks
        } DUMMYSTRUCTNAME;
        ULONG64     ProcessorTime;          // Processor Clock
        struct {
            ULONG   ClientContext;          // Reserved
            ULONG   Flags;                  // Event Flags
        } DUMMYSTRUCTNAME2;
    } DUMMYUNIONNAME4;
} EVENT_TRACE_HEADER, * PEVENT_TRACE_HEADER;

typedef struct _MOF_FIELD {
    ULONG64     DataPtr;    // Pointer to the field. Up to 64-bits only
    ULONG       Length;     // Length of the MOF field
    ULONG       DataType;   // Type of data
} MOF_FIELD, * PMOF_FIELD;

#endif

namespace base {
namespace win {

    typedef GUID EtwEventClass;
    typedef UCHAR EtwEventType;
    typedef UCHAR EtwEventLevel;
    typedef USHORT EtwEventVersion;
    typedef ULONG EtwEventFlags;

    // Base class is a POD for correctness.
    template <size_t N>
    struct EtwMofEventBase {
        EVENT_TRACE_HEADER header;
        MOF_FIELD fields[N];
    };

    // Utility class to auto-initialize event trace header structures.
    template <size_t N>
    class EtwMofEvent : public EtwMofEventBase<N> {
    public:
        typedef EtwMofEventBase<N> Super;

        // Clang and the C++ standard don't allow unqualified lookup into dependent
        // bases, hence these using decls to explicitly pull the names out.
        using EtwMofEventBase<N>::header;
        using EtwMofEventBase<N>::fields;

        EtwMofEvent()
        {
            memset(static_cast<Super*>(this), 0, sizeof(Super));
        }

        EtwMofEvent(const EtwEventClass& event_class, EtwEventType type,
            EtwEventLevel level)
        {
            memset(static_cast<Super*>(this), 0, sizeof(Super));
            header.Size = sizeof(Super);
            header.Guid = event_class;
            header.Class.Type = type;
            header.Class.Level = level;
            header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
        }

        EtwMofEvent(const EtwEventClass& event_class, EtwEventType type,
            EtwEventVersion version, EtwEventLevel level)
        {
            memset(static_cast<Super*>(this), 0, sizeof(Super));
            header.Size = sizeof(Super);
            header.Guid = event_class;
            header.Class.Type = type;
            header.Class.Version = version;
            header.Class.Level = level;
            header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
        }

        void SetField(size_t field, size_t size, const void* data)
        {
            // DCHECK(field < N);
            if ((field < N) && (size <= std::numeric_limits<uint32_t>::max())) {
                fields[field].DataPtr = reinterpret_cast<ULONG64>(data);
                fields[field].Length = static_cast<ULONG>(size);
            }
        }

        EVENT_TRACE_HEADER* get() { return &header; }

    private:
        DISALLOW_COPY_AND_ASSIGN(EtwMofEvent);
    };

    // Trace provider with Event Tracing for Windows. The trace provider
    // registers with ETW by its name which is a GUID. ETW calls back to
    // the object whenever the trace level or enable flags for this provider
    // name changes.
    // Users of this class can test whether logging is currently enabled at
    // a particular trace level, and whether particular enable flags are set,
    // before other resources are consumed to generate and issue the log
    // messages themselves.
    class BASE_EXPORT EtwTraceProvider {
    public:
        // Creates an event trace provider identified by provider_name, which
        // will be the name registered with Event Tracing for Windows (ETW).
        explicit EtwTraceProvider(const GUID& provider_name);

        // Creates an unnamed event trace provider, the provider must be given
        // a name before registration.
        EtwTraceProvider();
        virtual ~EtwTraceProvider();

        // Registers the trace provider with Event Tracing for Windows.
        // Note: from this point forward ETW may call the provider's control
        //    callback. If the provider's name is enabled in some trace session
        //    already, the callback may occur recursively from this call, so
        //    call this only when you're ready to handle callbacks.
        ULONG Register();
        // Unregisters the trace provider with ETW.
        ULONG Unregister();

        // Accessors.
        void set_provider_name(const GUID& provider_name)
        {
            provider_name_ = provider_name;
        }
        const GUID& provider_name() const { return provider_name_; }
        TRACEHANDLE registration_handle() const { return registration_handle_; }
        TRACEHANDLE session_handle() const { return session_handle_; }
        EtwEventFlags enable_flags() const { return enable_flags_; }
        EtwEventLevel enable_level() const { return enable_level_; }

        // Returns true iff logging should be performed for "level" and "flags".
        // Note: flags is treated as a bitmask, and should normally have a single
        //      bit set, to test whether to log for a particular sub "facility".
        bool ShouldLog(EtwEventLevel level, EtwEventFlags flags)
        {
            return NULL != session_handle_ && level >= enable_level_ && (0 != (flags & enable_flags_));
        }

        // Simple wrappers to log Unicode and ANSI strings.
        // Do nothing if !ShouldLog(level, 0xFFFFFFFF).
        ULONG Log(const EtwEventClass& event_class, EtwEventType type,
            EtwEventLevel level, const char* message);
        ULONG Log(const EtwEventClass& event_class, EtwEventType type,
            EtwEventLevel level, const wchar_t* message);

        // Log the provided event.
        ULONG Log(EVENT_TRACE_HEADER* event);

    protected:
        // Called after events have been enabled, override in subclasses
        // to set up state or log at the start of a session.
        // Note: This function may be called ETW's thread and may be racy,
        //    bring your own locking if needed.
        virtual void OnEventsEnabled() { }

        // Called just before events are disabled, override in subclasses
        // to tear down state or log at the end of a session.
        // Note: This function may be called ETW's thread and may be racy,
        //    bring your own locking if needed.
        virtual void OnEventsDisabled() { }

        // Called just after events have been disabled, override in subclasses
        // to tear down state at the end of a session. At this point it's
        // to late to log anything to the session.
        // Note: This function may be called ETW's thread and may be racy,
        //    bring your own locking if needed.
        virtual void PostEventsDisabled() { }

    private:
        ULONG EnableEvents(PVOID buffer);
        ULONG DisableEvents();
        ULONG Callback(WMIDPREQUESTCODE request, PVOID buffer);
        static ULONG WINAPI ControlCallback(WMIDPREQUESTCODE request, PVOID context,
            ULONG* reserved, PVOID buffer);

        GUID provider_name_;
        TRACEHANDLE registration_handle_;
        TRACEHANDLE session_handle_;
        EtwEventFlags enable_flags_;
        EtwEventLevel enable_level_;

        // We don't use this, but on XP we're obliged to pass one in to
        // RegisterTraceGuids. Non-const, because that's how the API needs it.
        static TRACE_GUID_REGISTRATION obligatory_guid_registration_;

        DISALLOW_COPY_AND_ASSIGN(EtwTraceProvider);
    };

} // namespace win
} // namespace base

#endif // BASE_WIN_EVENT_TRACE_PROVIDER_H_
